package com.hernie.piggymall.search.thread;

import java.util.concurrent.*;

/**
 * @Author HuangJp
 * @Date 2022/3/28
 * @Description 多线程
 */
public class ThreadTest {
    // 当前系统中池只有一两个，每个异步任务都提交给线程池执行
    // 使用Executors创建线程池
    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    /**
     * CompletableFuture 可以启动异步任务
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {   // runAsync 开启异步任务，没有返回值
//            System.out.println("当前线程：" + Thread.currentThread().getName());
//            int i = 10 / 2;
//            System.out.println(i);
//        }, executor);

        /**
         * 方法完成后的感知
         */
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { // supplyAsync 开启异步任务，有返回值
//            System.out.println("当前线程：" + Thread.currentThread().getName());
//            int i = 10 / 2;
//            System.out.println(i);
//            return i;
//        }, executor).whenComplete((res, excption) -> {  // whenComplete 执行成功之后接着用该线程执行的内容,能够得到线程执行的结果，和感知异常，但是没法修改返回值
//            // whenCompleteAsync 线程执行之后 用异步的方式执行
//            System.out.println("异步任务完成了……结果是：" + res + "；异常是：" + excption);
//        }).exceptionally(throwable -> { // exceptionally 能够感知到异常，并在发生异常的时候设置返回值
//            return 10;
//        });

        /**
         * 方法完成后的处理
         */
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getName());
//            int i = 10 / 2;
//            System.out.println(i);
//            return i;
//        }, executor).handle((res, thr) -> { // handle 异步任务执行完做的后续处理，可以得到异步任务执行的返回值和感知异常，并且能够给出返回值
//            if (res != null) {
//                return res * 2;
//            }
//            if (thr != null) {
//                return 0;
//            }
//            return 0;
//        });
//        System.out.println(future.get());

        /**
         * 线程串行化
         *  1、thenRunAsync/thenRun：不能感知到上一步执行的结果，也没有返回值
         *  2、thenAcceptAsync/thenAccept：能接收上一步结果，但是无返回值
         *  3、thenApplyAsync/thenApply：能接收上一步结果，并且有返回值
         */
//        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getName());
//            int i = 10 / 2;
//            System.out.println(i);
//            return i;
//        }, executor).thenApplyAsync(res -> {
//            System.out.println("任务2启动了……" + res);
//            return "Hello" + res;
//        }, executor);
//        String s = future.get();    // 阻塞方法，必须等异步执行结束才能拿到返回值
//                thenAcceptAsync(res -> {
//            System.out.println("任务2启动了……" + res);
//        });
//                thenRunAsync(() -> {   // thenRunAsync 前一个线程执行完之后再开一个线程执行后续任务  thenRun前一个线程执行完之后接着执行后续任务
//            System.out.println("任务2启动了……");
//        }, executor);

        /**
         * 两任务组合
         * 再两个异步任务都执行完之后再开始执行
         */
        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程：" + Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
            return i;
        }, executor);
        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程：" + Thread.currentThread().getName());
            return "hello";
        }, executor);
//        future01.runAfterBothAsync(future02, () -> {
//            System.out.println("任务3开始");
//        }, executor);
//        future01.thenAcceptBothAsync(future02,(f1, f2) -> {
//            System.out.println("任务3开始，任务1结果是：" + f1 + "；任务2结果是：" + f2);
//        } , executor);
//        CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
//            System.out.println("任务3开始，任务1结果是：" + f1 + "；任务2结果是：" + f2);
//            return "任务3开始，任务1结果是：" + f1 + "；任务2结果是：" + f2;
//        }, executor);

        /**
         * 两个异步任务只要有一个完成就开始执行
         * runAfterEitherAsync : 不感知结果，也没有返回值
         * acceptEitherAsync ： 能感知到结果，自己没有返回值
         * applyToEitherAsync ： 能感知到结果，自己有返回值
         */
//        future01.runAfterEitherAsync(future02, () -> {
//            System.out.println("任务3开始");
//        }, executor);
//        future01.acceptEitherAsync(future02,(res) -> {  // 这里的future01和future02返回值需要时同一个类型的，能接收其中一个线程的结果
//            System.out.println("任务3开始，上一步结果是：" + res);
//        } , executor);
//        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
//            System.out.println("任务3开始，上一步结果是：" + res);
//            return "任务3开始，上一步结果是：" + res;
//        }, executor);

        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.jpg";
        }, executor);
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "黑色+256G";
        }, executor);
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品介绍");
            return "华为";
        }, executor);
        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
        allOf.get(); // 等待所有结果完成
        System.out.println(futureImg.get() + "=>" + futureAttr.get() + "=>" + futureDesc.get());
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc); // 只要有一个线程执行完就返回
        anyOf.get();
        System.out.println(anyOf.get());    // 拿到其中一个异步任务的结果
    }

    // 线程池
    public static void thread(String[] args) {
        /**
         * 1、继承Thread
         * 2、实现Runnable接口
         * 3、实现Callable接口 + FutureTask （可以拿到返回结果，可以处理异常）
         * 4、线程池 [ExecutorService]
         *      给线程池直接提交任务
         *    4.1、创建线程池
         *        方式一：Executors工具类
         *        方式二：原生创建
         *
         * 区别： 继承Thread、实现Runnable接口不能得到返回值，实现Callable接口可以得到返回值，这三种方式都不能控制资源
         *      线程池可以控制资源，性能稳定
         *
         *    Future 可以获取到异步结果
         */
//        1、继承Thread
//        Thread01 thread01 = new Thread01();
//        thread01.start(); //  启动线程
        // 匿名内部类方式
//        new Thread(() -> System.out.println("hello")).start();
//        2、实现Runnable接口
//        Runable01 runable01 = new Runable01();
//        new Thread(runable01).start();

//        3、实现Callable接口 + FutureTask （可以拿到返回结果，可以处理异常）
//        FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
//        new Thread(futureTask).start();
        // 阻塞等待整个线程执行完成，获取返回结果
//        Integer integer = futureTask.get();
//        System.out.println(integer);
        // 在实际业务代码中，以上三种启动线程池的方式都不用，【将所有的多线程异步任务都交给线程池执行】

        // 使用上面创建的线程池执行任务       execute方法和submint方法都是提交任务，submint方法有返回值，execute方法没有返回值
//        executorService.execute(new Runable01());

        /**
         * 创建线程池（自定义原生线程池）
         * 七大参数：
         *    int corePoolSize, 核心线程数，线程池创建好以后就准备就绪的线程数量，等待接收执行异步任务
         *                      只要线程不销毁就一直存在，除非设置了allowCoreThreadTimeOut超时属性
         *    int maximumPoolSize, 最大线程数量；控制资源并发
         *    long keepAliveTime,   存活时间，如果当前运行的线程数量大于核心数量，会在空闲时间大于存活时间后释放核心线程以外的多余线程
         *    TimeUnit unit,    存活时间单位
         *    BlockingQueue<Runnable> workQueue,  阻塞队列，如果任务很多，就会将目前多的任务放在队列里面，只要用空闲的线程就会去队列里取出新的任务去执行
         *    ThreadFactory threadFactory,  线程的创建工厂
         *    RejectedExecutionHandler handler  拒绝策略，如果队列满了，按照指定的拒绝策略拒绝执行任务
         *                                      AbortPolicy（默认）：丢弃任务并抛出 RejectedExecutionException 异常。
         *                                      CallerRunsPolicy：由调用线程处理该任务。不抛弃任务，改用run方法同步调用
         *                                      DiscardPolicy：丢弃任务，但是不抛出异常。可以配合这种模式进行自定义的处理方式。
         *                                      DiscardOldestPolicy：丢弃队列最早的未处理任务，然后重新尝试执行任务。
         *
         * 工作顺序:
         *     1、线程池创建，准备好 core 数量的核心线程，准备接受任务
         *          (1) 、core 满了，就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
         *          (2) 、阻塞队列满了，就直接开新线程执行，最大只能开到 max 指定的数量
         *          (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
         *          (4) 、如果线程数开到了 max 的数量，还有新任务进来，就会使用 reject 指定的拒绝策略进行处理
         *
         *       new LinkedBlockingDeque<>() 默认是Integer的最大值，为了避免内存溢出，建议指定队列最大数量
         *
         *      Executors工具类提供的线程池
         *          定长线程池（FixedThreadPool）      核心线程数和最大线程数一致
         *          定时线程池（ScheduledThreadPool ） 定时任务的线程池
         *          可缓存线程池（CachedThreadPool）    核心线程数为0，所有的线程都可回收
         *          单线程化线程池（SingleThreadExecutor）   核心线程数和最大线程数数都是1，队列是一个无界队列，后台利用单线程挨个执行任务
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

    }

    //        1、继承Thread
    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
        }
    }

//    2、实现Runnable接口
    public static class Runable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
        }
    }
//    3、实现Callable接口
    public static class Callable01 implements Callable<Integer>{  // 泛型指定返回值类型

        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程：" + Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
            return i;
        }
    }
}
